package com.semidata.rtd.core.service;

import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.ConcurrentMap;

import com.semidata.rtd.core.log.Logger;

import sun.misc.Signal;

@SuppressWarnings("restriction")
public class ServiceManager {

    private static final ConcurrentMap<Class<? extends Service>, Service> table = new ConcurrentHashMap<>();
    private static final ConcurrentLinkedDeque<Service> order = new ConcurrentLinkedDeque<>();
    private static Object[] lock = new Object[0];

    @SuppressWarnings("unchecked")
    public static <T extends Service> T getService(Class<T> name) {
        return (T) table.get(name);
    }

    public static void register(Class<? extends Service> name, Service service) {
        if (table.containsKey(name)) {
            throw new ServiceException("class " + name.getName() + " has been registered");
        }

        if (!table.values().contains(service)) {
            order.addLast(service);
        }
        table.put(name, service);
    }

    @SuppressWarnings("unchecked")
    public static void register(Service service) {

        Class<?> currentClass = service.getClass();

        Class<?> serviceClass = Service.class;

        while (serviceClass.isAssignableFrom(currentClass)) {
            if (currentClass.isAnnotationPresent(RegisterService.class) || currentClass == service.getClass())
                register((Class<? extends Service>) currentClass, service);
            currentClass = currentClass.getSuperclass();
        }
    }

    private static class SignalHandler implements sun.misc.SignalHandler {

        @Override
        public void handle(Signal signal) {

            Iterator<Service> it = order.descendingIterator();
            while (it.hasNext()) {
                Service service = it.next();
                try {
                    service.stop();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

            table.clear();
            order.clear();

            synchronized (lock) {
                lock.notifyAll();
            }
        }
    }

    public static void startAll() {

        for (Service service : order) {
            try {
                service.start();
            } catch (Exception e) {
                Logger.log("ERROR", e);
                Iterator<Service> it = order.descendingIterator();
                boolean flag = false;
                while (it.hasNext()) {
                    Service s = it.next();
                    if (flag) {
                        try {
                            service.stop();
                        } catch (Exception e1) {
                            e1.printStackTrace();
                        }
                    } else {
                        if (s == service)
                            flag = true;
                    }
                }
                table.clear();
                order.clear();
                throw e;
            }
        }

        SignalHandler handler = new SignalHandler();
        Signal.handle(new Signal("INT"), handler);
        Signal.handle(new Signal("TERM"), handler);
    }

    @SuppressWarnings("unchecked")
    public static <T> void send(Class<? extends Service> name, T msg) {
        Service service = table.get(name);
        if (service == null)
            return;
        if (service instanceof SendableService) {
            ((SendableService<T>) service).send(msg);
        }
    }

    public static void sync() {
        synchronized (lock) {
            try {
                lock.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
